#include "stxextprotocol.h"
#include "plat/bitconverter.h"
#include "log/logger.h"
#include "lib/stringex.h"

std::shared_ptr<STXETXCommMessage> STXETXCommMessage::CreateMessage(uint8_t srcAddr, uint8_t dstAddr, uint32_t cmd, uint8_t *data, int dataLen)
{
    auto msg = CreateMessage(cmd, data, dataLen);
    msg->SetAddress(srcAddr, dstAddr);

    return msg;
}

std::shared_ptr<STXETXCommMessage> STXETXCommMessage::CreateMessage(uint32_t cmd, uint8_t *data, int dataLen)
{
    auto msg = std::shared_ptr<STXETXCommMessage>(new STXETXCommMessage());

    msg->InitMessage(cmd, data, dataLen);
    return msg;
}

STXETXCommMessage& STXETXCommMessage::InitMessage(uint32_t cmd, uint8_t *data, int dataLen)
{
    if(dataLen > PROTOCOL_DATA_MAX_LEN)
    {
        return *this;
    }

    if(data == nullptr)
    {
        dataLen = 0;
    }

    Cmd = cmd;
    DataLength = dataLen;
    memcpy((void*)DataBuffer, data, dataLen);

    return *this;
}

STXETXCommMessage& STXETXCommMessage::SetAddress(uint8_t srcAddr, uint8_t dstAddr)
{
    SrcAddr = srcAddr;
    DstAddr = dstAddr;

    return *this;
}

std::string STXETXCommMessage::ToString()
{
    std::string res = StringEx::Format("FN=%d, SrcAddr=%d, DstAddr=%02X, Cmd=%04X, DLen=%d", 
                                        FrameNo, SrcAddr, DstAddr, Cmd, DataLength);

    return res;
}

STXETXProtocol::STXETXProtocol()
{
}

STXETXProtocol::~STXETXProtocol()
{
}

//为了减少内存使用，这里共用buffer
int STXETXProtocol::Packet(std::shared_ptr<IProtocolMessage> msg)
{
    if(msg == nullptr)
        return 0;

    auto tagMsg = msg->As<STXETXCommMessage>();
    if(tagMsg == nullptr)
        return 0;

    if(tagMsg->DataLength > PROTOCOL_DATA_MAX_LEN)
        return 0;

    int index = PROTOCOL_FRAME_MAX_LEN;
    uint8_t sum = 0;
    tagMsg->FrameNo = m_frameNo++; //外部调用有加锁，这里不用加锁了
    //frame
    m_sendBuffer[index++] = tagMsg->FrameType;
    m_sendBuffer[index++] = tagMsg->FrameNo;
    m_sendBuffer[index++] = tagMsg->SrcAddr;
    m_sendBuffer[index++] = tagMsg->DstAddr;
    BitConverter::ToBytes(tagMsg->Cmd, m_sendBuffer, index);index += sizeof(tagMsg->Cmd);
    m_sendBuffer[index++] = tagMsg->DataLength;
    
    for(int i = 0; i < tagMsg->DataLength; i++)
    {
        m_sendBuffer[index++] = tagMsg->DataBuffer[i];
    }
    if(m_checkSumEn)
    {
        for(int i = PROTOCOL_FRAME_MAX_LEN; i < index; i++)
        {
            sum += m_sendBuffer[i];
        }
    }
    m_sendBuffer[index++] = sum;

    //转码
    int sendIndex = 0;
    m_sendBuffer[sendIndex++] = PROTOCOL_STX;
    for(int i = PROTOCOL_FRAME_MAX_LEN; i < index; i++)
    {
        uint8_t b = m_sendBuffer[i];
        if(PROTOCOL_IS_ESC(b)) 
        {
            m_sendBuffer[sendIndex++] = PROTOCOL_ESC;
            m_sendBuffer[sendIndex++] = b + PROTOCOL_DETA;
        }
        else
        {
            m_sendBuffer[sendIndex++] = b;
        }
    }
    m_sendBuffer[sendIndex++] = PROTOCOL_ETX;

    return sendIndex;
}

uint8_t *STXETXProtocol::GetSendBuffer()
{
    return m_sendBuffer;
}

std::shared_ptr<IProtocolMessage> STXETXProtocol::Parse(uint8_t b)
{
    std::shared_ptr<IProtocolMessage> msg = nullptr;
    
    if(m_recvLen >= PROTOCOL_FRAME_MAX_LEN)
    {
        //error
        LogDebug()<<"m_recvLen len error, Reset";
        Reset();
    }

    if(b == PROTOCOL_STX)
    {
        m_isRecving = true;
        m_recvLen = 0;
        m_prevRecvByte = -1;
        m_recvBuffer[m_recvLen++] = b;
    }
    else if(m_isRecving)
    {
        if(b == PROTOCOL_ETX)
        {
            m_isRecving = false;
            m_recvBuffer[m_recvLen++] = b;
            msg = ParseHandle();
        }
        else if(b == PROTOCOL_ESC)
        {
            ;//pass
        }
        else if(m_prevRecvByte == PROTOCOL_ESC)
        {
            m_recvBuffer[m_recvLen++] = b - PROTOCOL_DETA;    
        }
        else
        {
            m_recvBuffer[m_recvLen++] = b;
        }
    }
    
    m_prevRecvByte = b;
    return msg;
}

void STXETXProtocol::Reset()
{
    m_isRecving = false;
    m_recvLen = 0;
    m_prevRecvByte = -1;
}

bool STXETXProtocol::IsAck(std::shared_ptr<IProtocolMessage> sendMsg, std::shared_ptr<IProtocolMessage> recvMsg)
{
    FW_UNUSED(sendMsg);
    FW_UNUSED(recvMsg);
    
    return false;
}

bool STXETXProtocol::IsResponse(std::shared_ptr<IProtocolMessage> sendMsg, std::shared_ptr<IProtocolMessage> recvMsg)
{
    if(sendMsg == nullptr || recvMsg == nullptr)
        return false;

    auto tagSendMsg = sendMsg->As<STXETXCommMessage>();
    auto tagRecvMsg = recvMsg->As<STXETXCommMessage>();
    
    FW_ASSERT(tagSendMsg);
    FW_ASSERT(tagRecvMsg);
       
    return tagSendMsg->Cmd == tagRecvMsg->Cmd && 
           tagSendMsg->DstAddr == tagRecvMsg->SrcAddr;
}

void STXETXProtocol::SetCheckSumEnabled(bool enabled)
{
    m_checkSumEn = enabled;
}

bool STXETXProtocol::GetCheckSumEnabled()
{
    return m_checkSumEn;
}

bool STXETXProtocol::CheckSum()
{
    //计算校验和
    uint8_t sum = 0;
    uint8_t recvSum = m_recvBuffer[m_recvLen - 2];
    for(int i = 1; i <= m_recvLen - 3; i++)
    {
        sum += m_recvBuffer[i];
    }

    return sum == recvSum;
}

std::shared_ptr<IProtocolMessage> STXETXProtocol::ParseHandle()
{
    if(m_recvLen < PROTOCOL_FRAME_MIN_LEN)
        return nullptr;  
    
    if(m_checkSumEn)
    {
        if(!CheckSum())
        {
            DumpErrorMessage();
            return nullptr;
        }
    }

    auto msg = std::shared_ptr<STXETXCommMessage>(new STXETXCommMessage());
    msg->FrameType = m_recvBuffer[1];
    msg->FrameNo = m_recvBuffer[2];
    msg->SrcAddr = m_recvBuffer[3];
    msg->DstAddr = m_recvBuffer[4];
    msg->Cmd = BitConverter::ToValue<uint16_t>(m_recvBuffer, 5);
    msg->DataLength = m_recvBuffer[7];
    
    for(int i = 0; i < msg->DataLength; i++)
    {
        msg->DataBuffer[i] = m_recvBuffer[8+i];
    }

    return msg;      
}

void STXETXProtocol::DumpErrorMessage()
{
    LogDebug()<<"crc error";
    for(int i = 0; i < m_recvLen; i++)
    {
        LogDebug().NoFormat().NoNewline()<<StringEx::Format("%02X ", m_recvBuffer[i]);
    }
    LogDebug().NoFormat()<<"";
}
